package move_utils

import (
	"confs/gen/common"
	"game/app/actors/schemas"
	"game/app/systems/map_system/map_utils"
	"game/app/systems/math_system/math_defines"
	"game/app/systems/math_system/math_utils"
	"game/app/systems/move_system/move_defines"
	"gos/modules/logger"
	"math"
)

// MoveUtilsSystem 移动工具
type MoveUtilsSystem struct{}

type IMoveUtilsSystem interface {
	MakeMapData(state *schemas.MoveState, mb *schemas.MoveEntity) *common.MapData
	// MakeMoveDetailData 转化
	MakeMoveDetailData(state *schemas.MoveState, mb *schemas.MoveEntity) *common.MoveDetailData

	// GetPosition 获取中心坐标
	GetPosition(mo *common.MapObject) math_utils.Vector2D
	// GetScale   获取占地区域范围
	GetScale(mo *common.MapObject) math_utils.ZNumber
	// GetCollision 获取碰撞范围
	GetCollision(mo *common.MapObject) math_utils.ZNumber

	// LineInterGrid 直线相交格子
	LineInterGrid(start math_utils.Vector2D, end math_utils.Vector2D) (bool, []int32)
	// AddInterGrid 直线相交格子集合
	AddInterGrid(gridIdx *[]int32, x math_utils.ZNumber, y math_utils.ZNumber, steep bool, swap bool)
	// CalMoveCriclePath 计算直线和圆的交点
	CalMoveCriclePath(start math_utils.Vector2D, target math_utils.Vector2D, center math_utils.Vector2D,
		cl math_utils.ZNumber, paths *[]math_utils.Vector2D) int32
	// RotationMatrix 旋转矩阵
	RotationMatrix(pos1 math_utils.Vector2D, pos2 math_utils.Vector2D, cpos math_utils.Vector2D, paths *[]math_utils.Vector2D)
	// 是否属于区域范围内
	IsRelation(start math_utils.Vector2D, target math_utils.Vector2D, center math_utils.Vector2D, cl math_utils.ZNumber, cvt *math_utils.Vector2D) bool

	// CalcOutPos
	CalcOutPos(state *schemas.MapState, curPos math_utils.Vector2D, curCollision math_utils.ZNumber, targetPos math_utils.Vector2D, targetCollision math_utils.ZNumber) (*common.Vector2D, bool)

	// FindRealAnchorPoint 寻找锚点
	FindRealAnchorPoint(targetPos math_utils.Vector2D, dis math_utils.ZNumber, anchorPoints []math_utils.Vector2D, realAnchorPoints *[]math_utils.Vector2D) bool

	// 寻找移动到目标的合理位置
	FindOutPos(state *schemas.MapState, curPos math_utils.Vector2D, curCollision math_utils.ZNumber, targetPos math_utils.Vector2D, targetCollision math_utils.ZNumber) (*math_utils.Vector2D, bool)
	// 生成合理位置
	MakeAnchorPoint(angle int32, anchorPoints *[]math_utils.Vector2D) bool
}

var MoveUtils IMoveUtilsSystem = MoveUtilsSystem{}

// GetScale
func (m MoveUtilsSystem) GetScale(mo *common.MapObject) math_utils.ZNumber {
	return math_utils.MakeByReal(mo.Scale)
}

// GetCollision
func (m MoveUtilsSystem) GetCollision(mo *common.MapObject) math_utils.ZNumber {
	return math_utils.MakeByReal(mo.Collision)
}

// GetPosition
func (m MoveUtilsSystem) GetPosition(mo *common.MapObject) math_utils.Vector2D {
	return math_utils.MakeVector2dByProto(mo.CenterPos)
}

// IsRelation 区域内是否存在
func (m MoveUtilsSystem) IsRelation(start math_utils.Vector2D, target math_utils.Vector2D, center math_utils.Vector2D, cl math_utils.ZNumber, cvt *math_utils.Vector2D) bool {
	v := target.Sub(start)
	if v.LengthSquare().IsEq(0) {
		return false
	}

	cv := center.Sub(start)
	dir := v.Dot(cv)
	if dir.IsLe(0) {
		return false
	}

	clls := cl.Mul(cl)
	tv := center.Sub(target)
	if cv.LengthSquare().Lq(clls) || tv.LengthSquare().Lq(clls) {
		return false
	}

	vt := cv.Sub(v.Mul(cv.Dot(v)).Dlv(v.LengthSquare()))
	vtl := vt.LengthSquare()
	if vtl.Mq(clls) {
		return false
	}

	if dir.IsEq(0) {
		return false
	}

	dir = cv.Neg().Dot(tv.Neg())
	if dir.IsMe(0) {
		return false
	}

	*cvt = cv.Sub(vt)
	return true
}

// MakeMapData
func (m MoveUtilsSystem) MakeMapData(state *schemas.MoveState, mb *schemas.MoveEntity) *common.MapData {
	paths := []*common.Vector2D{}
	for _, path := range mb.MoveCirclePaths {
		paths = append(paths, path.ToVector2D())
	}

	for _, path := range mb.Paths {
		paths = append(paths, path.ToVector2D())
	}

	return &common.MapData{
		Id:         mb.Id,
		TargetPos:  mb.TargetPos.ToVector2D(),
		MoveStatus: mb.MoveStatus,
		Paths:      paths,
	}
}

// MakeMoveDetailData
func (m MoveUtilsSystem) MakeMoveDetailData(state *schemas.MoveState, mb *schemas.MoveEntity) *common.MoveDetailData {
	paths := []*common.Vector2D{}
	for _, path := range mb.Paths {
		paths = append(paths, path.ToVector2D())
	}

	movePaths := []*common.Vector2D{}
	for _, path := range mb.MoveCirclePaths {
		movePaths = append(movePaths, path.ToVector2D())
	}

	return &common.MoveDetailData{
		Id:              mb.Id,
		TargetPos:       mb.TargetPos.ToVector2D(),
		Speed:           mb.MoveSpeed.Value,
		CurPos:          mb.CurPos.ToVector2D(),
		Direction:       mb.Direction.ToVector2D(),
		MoveStatus:      mb.MoveStatus,
		Paths:           paths,
		MoveCirclePaths: movePaths,
	}
}

// AddInterGrid
func (m MoveUtilsSystem) AddInterGrid(gridIdx *[]int32, x math_utils.ZNumber, y math_utils.ZNumber, steep bool, swap bool) {
	var index int32
	if steep {
		index = map_utils.MapUtils.PointToPos(y.ToGrid(), x.ToGrid())

	} else {
		index = map_utils.MapUtils.PointToPos(x.ToGrid(), y.ToGrid())
	}

	if swap {
		*gridIdx = append([]int32{index}, *gridIdx...)
	} else {
		*gridIdx = append(*gridIdx, index)
	}
}

// LineInterGrid
func (m MoveUtilsSystem) LineInterGrid(start math_utils.Vector2D, end math_utils.Vector2D) (bool, []int32) {
	x0 := start.X
	y0 := start.Y
	x1 := end.X
	y1 := end.Y

	steep := false
	if y1.Sub(y0).Abs().Me(x1.Sub(x0).Abs()) {
		math_utils.Swap(&x0, &y0)
		math_utils.Swap(&x1, &y1)
		steep = true
	}

	swap := false
	if x0.Me(x1) {
		math_utils.Swap(&x0, &x1)
		math_utils.Swap(&y0, &y1)
		swap = true
	}

	gridIndex := []int32{}
	gridSize := math_utils.MakeZNumberByInt32(math_defines.GridSize)
	if y0.ToGrid() == y1.ToGrid() {
		for x := x0; x.Le(x1.Ceil(gridSize)); x = x.Add(gridSize).Floor(gridSize) {
			m.AddInterGrid(&gridIndex, x, y0, steep, swap)
		}
		return true, gridIndex
	}

	deltaX := x1.Sub(x0)
	deltaY := y1.Sub(y0)

	stepY := math_utils.MakeZNumberByInt32(0x7fffffff)
	if deltaX.IsNe(0) {
		stepY = deltaY.Dlv(deltaX)
	}

	preY := y0
	preX := x0

	for x := x0; x.Lq(x1.Ceil(gridSize)); x = x.Add(gridSize).Floor(gridSize) {
		y := y0.Add(x.Sub(x0).Mul(stepY))
		if y.ToGrid() != preY.ToGrid() {
			m.AddInterGrid(&gridIndex, preX, y, steep, swap)
		}

		m.AddInterGrid(&gridIndex, x, y, steep, swap)
		preX = x
		preY = y
	}
	return true, gridIndex
}

// CalMoveCriclePath
func (m MoveUtilsSystem) CalMoveCriclePath(start math_utils.Vector2D, target math_utils.Vector2D, center math_utils.Vector2D,
	cl math_utils.ZNumber, paths *[]math_utils.Vector2D) int32 {
	if cl.IsEq(0) {
		return move_defines.CalPathResultNone
	}

	v := target.Sub(start)
	vls := v.LengthSquare()
	if vls.IsEq(0) {
		return move_defines.CalPathResultNone
	}

	cv := center.Sub(start)
	cvl := cv.Length()
	dir := v.Dot(cv)
	if dir.IsLe(0) {
		return move_defines.CalPathResultNone
	}

	tv := target.Sub(center)
	tvl := tv.Length()
	if tvl.IsEq(0) {
		return move_defines.CalPathResultNone
	}

	tvls := tvl.Mul(tvl)
	if cvl.Lq(cl) && tvl.Lq(cl) {
		return move_defines.CalPathResultNone
	} else if tvl.Lq(cl) {
		pv := cv.Normal().Mul(cl)
		temp := center.Sub(pv)
		*paths = append(*paths, start)
		*paths = append(*paths, temp)
		return move_defines.CalPathResultPopTarget
	}

	cross := cv.Cross(v)
	var inter1 math_utils.Vector2D
	var inter2 math_utils.Vector2D

	if cross.IsEq(0) {
		base := cv.Normal().Mul(cl)
		inter1 = center.Sub(base)

		cos := cl.Dlv(tvl)
		lq := (tvls.Sub(cl.Mul(cl))).Sqrt()
		sin := lq.Dlv(tvl)

		mt := math_utils.MakeMatrix2ByZNumber(cos, sin.Neg(), sin, cos)
		pv := tv.MulByMatrix(mt).Normal().Mul(cl)
		inter2 = center.Add(pv)
	} else {
		v := target.Sub(start)
		vls := v.LengthSquare()

		cv := center.Sub(start)

		tv := target.Sub(center)
		tvl := tv.Length()
		tvls := tvl.Mul(tvl)

		clls := cl.Mul(cl)
		temp := v.Mul(cv.Dot(v)).Dlv(vls)
		p := cv.Sub(temp)
		pls := p.LengthSquare()

		if pls.Mq(clls) {
			return move_defines.CalPathResultNone
		}

		vl := clls.Sub(pls).Sqrt()
		vs := v.Normal().Mul(vl)
		iv := vs.Neg().Sub(p)
		inter1 = center.Add(iv)

		cos := cl.Dlv(tvl)
		lq := tvls.Sub(cl.Mul(cl)).Sqrt()
		sin := lq.Dlv(tvl)

		mt := math_utils.MakeMatrix2ByZNumber(cos, sin.Neg(), sin, cos)
		pv := tv.MulByMatrix(mt).Normal().Mul(cl)
		dir := pv.Dot(p.Neg())

		if dir.IsLe(0) {
			mt = math_utils.MakeMatrix2ByZNumber(cos, sin, sin.Neg(), cos)
			pv = tv.MulByMatrix(mt).Normal().Mul(cl)
		}
		inter2 = center.Add(pv)
	}

	m.RotationMatrix(inter1, inter2, center, paths)
	*paths = append([]math_utils.Vector2D{inter1}, *paths...)
	*paths = append(*paths, inter2)
	return move_defines.CalPathResultMoveToTarget
}

// RotationMatrix
func (m MoveUtilsSystem) RotationMatrix(pos1 math_utils.Vector2D, pos2 math_utils.Vector2D, center math_utils.Vector2D, paths *[]math_utils.Vector2D) {
	v1 := center.Sub(pos1)
	v2 := center.Sub(pos2)
	v3 := pos2.Sub(pos1)

	if v2.Length().IsEq(0) || v1.Length().IsEq(0) {
		return
	}

	result := v2.Dot(v1).Dlv(v2.Length().Mul(v1.Length()))
	arv := math.Acos(result.ToFloat64()) * 180 / math.Pi

	count := int(arv / 20.0)
	if count > 1 {
		dir := 20.0
		dir2 := v1.Cross(v3)
		if dir2.IsLe(0) {
			dir = -dir
		}

		sin := math_utils.MakeZNumberByFloat64(math.Sin(dir / 180.0 * math.Pi))
		cos := math_utils.MakeZNumberByFloat64(math.Cos(dir / 180.0 * math.Pi))
		mt := math_utils.MakeMatrix2ByZNumber(cos, sin.Neg(), sin, cos)
		temp := v1
		for i := 0; i < count; i++ {
			temp = temp.MulByMatrix(mt)
			*paths = append(*paths, center.Sub(temp))
		}
	}
}

// CalcOutPos 计算目标锚点
func (s MoveUtilsSystem) CalcOutPos(state *schemas.MapState, curPos math_utils.Vector2D, curCollision math_utils.ZNumber, targetPos math_utils.Vector2D, targetCollision math_utils.ZNumber) (*common.Vector2D, bool) {
	outPos, ok := s.FindOutPos(state, curPos, curCollision, targetPos, targetCollision)
	if !ok {
		return nil, false
	}

	logger.WARN("CalcOutPos:", targetPos, " ", outPos, " LenthSquare:", targetPos.Sub(*outPos).LengthSquare())
	return outPos.ToVector2D(), true
}

// FindOutPos 查找一个合理点位
func (m MoveUtilsSystem) FindOutPos(state *schemas.MapState, curPos math_utils.Vector2D, curCollision math_utils.ZNumber, targetPos math_utils.Vector2D, targetCollision math_utils.ZNumber) (*math_utils.Vector2D, bool) {
	anchorPoints := []math_utils.Vector2D{}
	if !m.MakeAnchorPoint(math_defines.AnchorPonitAngle, &anchorPoints) {
		return nil, false
	}

	if len(anchorPoints) <= 0 {
		return &curPos, true
	}

	dis := curCollision.Add(targetCollision)
	dv := targetPos.Sub(curPos)
	if dis.Mul(dis).Mq(dv.LengthSquare()) {
		return &targetPos, true
	}

	realAnchorPoint := []math_utils.Vector2D{}
	if !m.FindRealAnchorPoint(targetPos, dis, anchorPoints, &realAnchorPoint) {
		return &curPos, true
	}

	var dir math_utils.ZNumber
	var findPos math_utils.Vector2D
	find := false
	for _, pos := range realAnchorPoint {
		if state.MapTerrain.IsBlock(pos.X.ToGrid(), pos.X.ToGrid()) {
			continue
		}

		temp := dv.Dot(pos.Sub(curPos))
		if !find || temp.Le(dir) {
			find = true
			dir = temp
			findPos = pos
		}
	}

	if !find {
		return &curPos, true
	}

	return &findPos, true
}

// FindRealAnchorPoint 获取锚点
func (m MoveUtilsSystem) FindRealAnchorPoint(targetPos math_utils.Vector2D, dis math_utils.ZNumber, anchorPoints []math_utils.Vector2D, realAnchorPoints *[]math_utils.Vector2D) bool {
	if dis.IsEq(0) {
		return false
	}

	for _, point := range anchorPoints {
		*realAnchorPoints = append(*realAnchorPoints, targetPos.Add(point.Mul(dis)))
	}
	return true
}

// MakeAnchorPoint 生成一个合理锚点位置
func (m MoveUtilsSystem) MakeAnchorPoint(angle int32, anchorPoints *[]math_utils.Vector2D) bool {
	if angle == 0 {
		return false
	}

	if 360%angle != 0 {
		return false
	}

	sin := math_utils.MakeZNumberByFloat64(math.Sin(float64(angle) / 180.0 * math.Pi))
	cos := math_utils.MakeZNumberByFloat64(math.Cos(float64(angle) / 180.0 * math.Pi))

	mt := math_utils.MakeMatrix2ByZNumber(cos, sin.Neg(), sin, cos)
	temp := math_utils.MakeVecto2dByInt64(1, 0)
	count := 360 / angle
	for i := 0; i < int(count); i++ {
		temp = temp.MulByMatrix(mt)
		*anchorPoints = append(*anchorPoints, temp)
	}
	return true
}
